/*
 * Some shit code By Zirina.
 * 2022/02/28 
 * 未竣工.
 */


/*
 * To-do list
 * 1. 子元素根据相对根元素的坐标，设置一个标记，表示向左 / 向右。
 * 3. 单击画布空白处拖动画布 (大概算完成了...)
 * 4. 将全局变量方法统筹进一个类里面
 * 5. 添加兄弟结点总是添加到父结点的数组末尾
 * 7. 收起时箭头旋转
 * 8. 卡死了orz 写的什么 shit (已完成性能优化)
 */

/* 全局配置 */
// gap 结点之间的间距
// sign 大纲前的箭头
const gap = 15;
const sign = '<a href="#"><svg t="1646396487032" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9489" width="200" height="200"><path d="M728.223744 520.22784a42.467328 42.467328 0 0 1-11.393024 20.503552L374.90688 882.65728c-16.662528 16.662528-43.677696 16.662528-60.340224 0s-16.662528-43.677696 0-60.340224L626.449408 510.43328 314.614784 198.598656c-16.662528-16.662528-16.662528-43.677696 0-60.340224 16.661504-16.662528 43.676672-16.662528 60.3392 0L716.879872 480.18432c10.860544 10.860544 14.642176 26.120192 11.343872 40.04352z" fill="#707070" p-id="9490"></path></svg></a>';


/* 获取 HTMLElement */
const canvas = document.getElementById("canvas");
const curveSet = document.getElementById('curveSet');
const outline = document.getElementById('outline');
const addNodeBar = document.querySelector(".tool[data-index='1']");
const title = document.querySelector('.title');
const header = document.querySelector('header');
const uploadImageBtn = document.querySelector('#uploadImage button');
const uploadImage = uploadImageBtn.querySelector('input');
const imageIcon = document.querySelector('#uploadImage .icon')

function getCanvasOffset() {
  const canvasCoord = canvas.getBoundingClientRect();
  const canvasOffset = {
    left: canvasCoord.left - canvas.scrollLeft,
    top: canvasCoord.top - canvas.scrollTop,
  };
  return canvasOffset;
}


/* 状态 */
// let isDragging = false;
let isEditing = false;

// selectedItem 目标结点（一次只能有一个目标结点）
// inputItem 保存 input 临时输入框
let selectedItem = null;
let inputItem = null;
let docTitle = '未命名';
let canvasCoord = null;

// nodeIndex 结点编号 
// nodeMap 结点映射：寻找结点编号对应的 Item 实例
let nodeIndex = 0;
let nodeMap = new Map();

document.addEventListener("keydown", handleKeyDown);
canvas.addEventListener("dblclick", handleDblClick);
canvas.addEventListener("mousedown", handleMouseDown);
title.addEventListener('dblclick', handleTitleDblClick);
outline.addEventListener('click', handleOutLineClick);
addNodeBar.addEventListener("click", handleAddNodeBarClick);

class Item {
  constructor(content, level, parent) {
    this.content = content;   // 内容
    this.level = level;       // 级数
    this.id = nodeIndex++;    // id

    this.parent = parent;     // 父结点
    this.children = [];       // 子结点

    this.width = 0;           // 包括子结点的总宽度
    this.height = 0;          // 包括子结点的总高度

    this.childrenHeight = 0;  // 子结点的总高度
    this.offsetHeight = 0;    // 结点自身的高度
    this.init();
  }

  /* 添加结点 */
  // 添加子结点
  addChild(content) {
    let child = new Item(content, this.level + 1, this);
    this.children.push(child);
    this.updateProperty();
  }
  // 添加兄弟结点
  addBro(content) {
    this.parent.addChild(content);
  }

  /* 初始化 */
  init() {
    // 创建 html元素
    let div = document.createElement("div");
    div.innerHTML = this.content;
    div.className = "item";
    div.dataset.id = this.id;
    div.dataset.level = Math.min(this.level, 2);
    canvas.append(div);

    // 存入映射
    nodeMap.set(this.id + '', this);

    // 更新实例属性
    this.div = div;
    this.updateNode();
    this.height = this.offsetHeight;
    this.width = this.offsetWidth;
  }
  /**
   * 修改结点的内容
   * 会导致宽度 / 高度发生改变，要更新所有的父结点的宽度 / 高度。
   * @param {String} newContent 
   */
  set(newContent) {
    // 内容没变，不用重绘
    if (newContent == this.content) return;
    this.div.innerHTML = newContent;
    this.content = newContent;
    this.updateNode();
    this.updateProperty();
  }
  /* 更新当前结点的高度 / 宽度属性 */
  updateNode() {
    this.offsetHeight = this.div.offsetHeight;
    this.offsetWidth = this.div.offsetWidth;
  }
  /* 获取子结点的总高度 */
  getChildrenHeight() {
    let height = this.children.reduce((sum, c) => sum + c.height, 0);
    height += (this.children.length - 1) * gap;
    return height;
  }
  /* 获取子结点的总宽度 */
  getChildrenWidth() {
    let width = 0;
    this.children.forEach((c) => {
      if (c.width > width) width = c.width;
    })
    return width + 3 * gap;
  }
  /* 更新所有父结点的属性 */
  updateProperty() {
    let that = this;
    while (that) {
      that.childrenHeight = that.getChildrenHeight();
      // 当前结点的总高度 = max(子结点的总高度，当前结点的高度)
      // 当前结点的总宽度 = 子结点的总宽度 + 当前结点的宽度
      that.height = Math.max(that.childrenHeight, that.offsetHeight);
      that.width = that.getChildrenWidth() + that.offsetWidth;
      // 已遍历到根结点
      if (!that.parent) break;
      that = that.parent;
    }
    // 重新渲染根结点
    that.render();
  }

  /* 渲染 */
  render() {
    // ** 读取 **
    const coord = this.div.getBoundingClientRect();
    // ** 重排 **
    // 为根结点时
    // 更新 curveSet 的大小
    // 更新 canvasCoord
    // 更新 大纲
    if(this == rootItem) {
      updateSVG();
      canvasCoord = getCanvasOffset();
      getOutLine();
    }
    const canvasOffset = canvasCoord;

    // 当前结点左上角相对于 Canvas 容器的坐标
    const coordToCanvas = {
      x: coord.left - canvasOffset.left,
      y: coord.top - canvasOffset.top
    }

    // 如果根结点距 Canvas 容器上边界的距离 < 子结点总高度 / 2
    // 则发生了上边界的溢出
    let shiftY = 0, shiftX = 0;
    if (this == rootItem) {
      shiftY = Math.max((this.height - this.offsetHeight) / 2 + gap - coordToCanvas.y, 0)
      // ** 重排 **
      if (shiftY) this.div.style.top = coordToCanvas.y + shiftY + 'px';
    }
    // 当前结点的中右点
    const parentPoint = {
      y: coordToCanvas.y + this.offsetHeight / 2 + shiftY,
      x: coordToCanvas.x + this.offsetWidth,
    }
    // 子节点左上角坐标
    let curY = parentPoint.y - this.childrenHeight / 2;
    let curX = coordToCanvas.x + this.offsetWidth + 3 * gap;

    // ** 重排 **
    // 清空当前结点的 svg path
    clearPath(this.id);

    // ** 重排 **
    // 渲染子结点
    for (let c of this.children) {
      c.div.style.top = curY + (c.height - c.offsetHeight) / 2 + 'px';
      c.div.style.left = curX + 'px';
      // 子结点的左中点
      let childPoint = {
        x: curX,
        y: curY + c.height / 2,
      }
      // 创建 SVG 曲线
      // SVG 与 普通 HTML 结点的命名空间不同。
      const strokeWidth = this.level == 0 ? 2 : 1;
      const color = this.level == 0 ? 'salmon' : 'LightCoral';
      let path = document.createElementNS("http://www.w3.org/2000/svg", 'path');
      path.setAttribute('stroke', color);
      path.setAttribute('stroke-width', strokeWidth);
      path.setAttribute('fill', "transparent");
      path.setAttribute('data-parentId', this.id);
      path.setAttribute('d', `M ${parentPoint.x} ${parentPoint.y} Q ${parentPoint.x} ${childPoint.y} ${childPoint.x} ${childPoint.y}`);
      curveSet.append(path);
      // 递归，从子结点渲染
      c.render();
      curY += c.height + gap;
    }
  }
  // 移除结点及其子结点的 html 元素
  removeDiv() {
    this.div.remove();
    clearPath(this.id);
    this.children.forEach((c) => c.removeDiv());
  }
  // 删除结点实例
  delete() {
    const index = this.parent.children.indexOf(this);
    this.parent.children.splice(index, 1);
    this.removeDiv();
    this.updateProperty();
  }
  // 移动父结点时，改变子结点的透明度
  changeChildrenState() {
    for (let c of this.children) {
      c.div.classList.toggle('moving');
      c.changeChildrenState();
    }
  }

  /**
   * 生成大纲的子树
   * @returns 子树 html字符串
   */
  createTree() {
    let str = '<li>' + '<div class="li">';
    let childStr = '';
    for (let c of this.children) {
      childStr += c.createTree();
    }
    if (childStr.length) str += sign + this.content + '</div><ul>' + childStr + '</ul>';
    else str += this.content;
    return str + '</li>';
  }
}

const rootItem = new Item("主题", 0, null);

/**
 * 全局 document 监听键盘事件
 * 1. [Delete] 删除选中结点
 * 2. [Enter] 正在编辑时完成编辑 使 input 失去焦点
 */
function handleKeyDown(event) {
  if (!selectedItem && !isEditing) return;
  const key = event.key;
  console.log(key);
  if (selectedItem && key == 'Delete') {
    let item = getNode(selectedItem.dataset.id);
    item.delete();
    selectedItem = null;
  } else if (isEditing && key == 'Enter') {
    inputItem.blur();
  }
}

/**
 * canvas 内部的鼠标按下事件
 * 可能是：单击选中，拖动选中结点，[to-do list]拖动画布
 * bug: 编辑子结点时不好直接移动父结点（特性！）
 */
function handleMouseDown(event) {
  let target = event.target.closest(".item,#canvas");
  let isCanvas = (target == canvas);
  if (isEditing) return;

  // 通过监听是否发生 mousemove 来判断是否是 click 事件。
  let isClick = true;
  // isDragging = true;
  event.preventDefault();

  // 相对于画布, 画布可能发生了滚动， 因此通过 getCanvasOffset() 来获取真实的偏移
  // canvasOffset.x = canvas.getBoundingClientRect().left - canvas.scrollLeft
  const canvasOffset = getCanvasOffset();
  const targetCoord = target.getBoundingClientRect();
  // 鼠标点击位置相对于 结点 的偏移
  const shift = {
    x: event.clientX - targetCoord.left,
    y: event.clientY - targetCoord.top
  };

  let oldClientX = event.clientX, oldClientY = event.clientY;

  // 如果目标结点为根结点：移动结点
  // 如果为其他结点 [to-do list]
  // 如果为画布：移动画布
  if (target == rootItem.div) document.addEventListener("mousemove", handleItemMove);
  else if (isCanvas) document.addEventListener("mousemove", handleCanvasMove);

  document.addEventListener("mouseup", handleMouseUp);

  /**
   * 移动结点，禁止超出边界
   * @param {Number} clientX 
   * @param {Number} clientY 
   */
  function moveItemTo(clientX, clientY) {
    // clientX - shift.x 得到 div 相对于整个窗口的真实左上角坐标，然后转化为相对于 canvas 的坐标。 
    let left = clientX - shift.x - canvasOffset.left;
    let top = clientY - shift.y - canvasOffset.top;
    left = Math.max(2, left);
    top = Math.max(rootItem.height / 2, top);
    target.style.left = left + "px";
    target.style.top = top + "px";
  }

  function scrollCanvasTo(clientX, clientY) {
    // 画布此时对于窗口的真实左上角坐标 clientX - shift.x ， 因此 canvas 的 scrollLeft -= clientX - oldClientX
    let left = clientX - oldClientX;
    let top = clientY - oldClientY;
    oldClientX = clientX;
    oldClientY = clientY;
    canvas.scrollLeft -= left;
    canvas.scrollTop -= top;

  }

  /* 处理拖动事件 */
  function handleItemMove(e) {
    if (isClick == true && (Math.abs(shift.x - e.clientX + targetCoord.left) >= 10 || Math.abs(shift.y - e.clientY + targetCoord.top) >= 10)) {
      isClick = false;
      rootItem.changeChildrenState();
    }
    if (!isClick) {
      moveItemTo(e.clientX, e.clientY);
    }
  }
  function handleCanvasMove(e) {
    scrollCanvasTo(e.clientX, e.clientY);
  }

  /* 鼠标按键释放 */
  function handleMouseUp(e) {
    document.removeEventListener("mouseup", handleMouseUp);
    if (isCanvas) {
      document.removeEventListener("mousemove", handleCanvasMove);
      return;
    }
    // 点击事件
    if (isClick) {
      handleClick(target);
      // 拖动事件 
      // 因为目前只能拖动根结点，因此从根结点开始重新渲染
      // [to-do list] 如果之后需要拖动其他结点的功能，可以修改成目标元素开始
      // changeChildrenState() 给子结点加上透明遮罩
    } else {
      rootItem.render();
      rootItem.changeChildrenState();
    }
    // 移除监听
    document.removeEventListener("mousemove", handleItemMove);
  }
}


/**
 * canvas 内部的双击事件：修改结点内容 
 * @param {*} event 
 * @returns 
 */
function handleDblClick(event) {
  let target = event.target.closest(".item");
  if (!target || isEditing) return;

  // 计算 修改结点 相对于 canvas 的坐标
  const targetCoord = target.getBoundingClientRect();
  const canvasOffset = getCanvasOffset();

  // 更新状态
  isEditing = true;
  selectedItem = target;

  // 创建一个临时输入框 input
  // level 设置输入框的样式，跟随修改结点的样式
  let input = createInput(target.innerHTML, "item");
  input.dataset.level = target.dataset.level;
  input.style.left = targetCoord.left - canvasOffset.left + "px";
  input.style.top = targetCoord.top - canvasOffset.top + "px";

  inputItem = input;
  target.classList.add("transparent");
  canvas.append(input);
  input.focus();

  // 编辑完成
  input.onblur = () => {
    target.classList.remove("transparent");
    const item = getNode(target.dataset.id);
    item.set(input.textContent);
    endEditing();
  }
}

/**
 * title 双击事件：更改标题 
 */
function handleTitleDblClick() {
  isEditing = true;

  inputItem = createInput(docTitle, 'title');
  title.insertAdjacentElement('beforebegin', inputItem);
  title.classList.add('hidden');
  inputItem.focus();

  inputItem.onblur = (event) => {
    docTitle = inputItem.textContent;
    title.innerHTML = docTitle;
    title.classList.remove('hidden');
    endEditing();
  }
}
/**
 * 编辑结束
 */
function endEditing() {
  inputItem.remove();
  isEditing = false;
}
/**
 * outline 点击事件：折叠子项
 */
function handleOutLineClick(event) {
  let target = event.target.closest('a');
  if (!target) return;
  let hiddenUl = target.closest('li').querySelector('ul');
  if (hiddenUl) hiddenUl.classList.toggle('hidden');
}
/**
 * canvas 单击事件：选中一个结点
 */
function handleClick(target) {
  if (selectedItem) selectedItem.style.outline = null;
  if (selectedItem == target) {
    selectedItem = null;
  } else {
    target.style.outline = "2px solid #333"; //css 不能设置为单独的 class
    selectedItem = target;
  }
}
/**
 * addNodeBar 添加结点功能区的点击事件：添加结点
 */
function handleAddNodeBarClick(event) {
  let target = event.target.closest(".addNodeIcon");
  if (!target || !selectedItem) return;
  event.preventDefault();
  const parent = getNode(selectedItem.dataset.id);
  switch (target.id) {
    case "addChildNode":
      parent.addChild("子项");
      break;
    case "addBroNode":
      parent.addBro("子项");
      break;
  }
}
/**
 * 根据 id 查找结点
 * @param {Number} id 
 * @returns id 对应的结点
 */
function getNode(id) {
  return nodeMap.get(id + '');
}
/**
 * 根据给定的 内容 和 类名 生成一个临时的 input 输入框。
 * @param {String} content 
 * @param {String} className 
 * @returns 生成的 input 输入框
 */
function createInput(content, className) {
  let input = document.createElement("div");
  input.innerHTML = content;
  input.contentEditable = true;
  input.className = className;
  return input;
}
/**
 * 更新 curveSet 的 height 和 width
 * width = 思维导图的总宽度 + 画布的已滚动宽度（画布视图的左侧部分） + 思维导图距离画布视图左边界的距离 + 一些边距
 * height = 思维导图的总高度 + 画布的已滚动高度（画布视图的上方部分） + 思维导图距离画布视图上边界的距离 + 一些边距
 */
function updateSVG() {
  const rootCoord = rootItem.div.getBoundingClientRect();
  curveSet.setAttribute('width', rootItem.width + canvas.scrollLeft + rootCoord.left + 2 * gap);
  curveSet.setAttribute('height', rootItem.height + canvas.scrollTop + rootCoord.top + 2 * gap);
}

/**
 * 获取大纲
 */
function getOutLine() {
  let htmlStr = '<ul>' + rootItem.createTree() + '</ul>';
  outline.innerHTML = '';
  outline.insertAdjacentHTML('afterbegin', htmlStr);
}
/**
 * 根据给定 id 清除对应的 svg path
 * @param {Number} id 
 */
function clearPath(id) {
  let targets = document.querySelectorAll(`path[data-parentId='${id}']`);
  targets.forEach(t => t.remove());
}
/* 移动画布
 * [to-do list] 怎么移动好呢？不知道
 */
function moveCanvas(event) {

}


uploadImageBtn.onclick = () => uploadImage.click();
uploadImage.onchange = (event) => {
  let file = event.target.files[0];
  let reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = imageLoaded.bind(this, reader);
  console.log(file);
}

function imageLoaded(reader){
 
  if(selectedItem) selectedItem.append(img);
  else canvas.append(img);
}

function compressedImg(img){
  let canvas = document.createElement('canvas'),
  context = canvas.getContext('2d'),
  imageWidth = img.width / 2,
  imageHeight = img.height / 2,
  data = '';

  canvas.width = imageWidth;
  canvas.height = imageHeight;
  
  context.drawImage(img, 0, 0, imageWidth, imageHeight);
  data = canvas.toDataURL('image/jpeg');
}